import sys
import json
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5 import QtCore, QtGui, QtWidgets 
import ipmsg 
import threading 
import ipmsg_protocol  as pt 
import os 
from resource import *

class usr_list_item(QListWidgetItem):
    def __init__(self):
        super(usr_list_item, self).__init__() 
    
    def __hash__(self):
        return hash(id(self))
class msg_item:
    Left,Right = range(2)

class msg_txt_item(msg_item, QWidget):
    def __init__(self, usr, data):
        super(msg_txt_item, self).__init__() 
# <<<<<<< HEAD
        self.msg_body = QTextEdit(data['data'][0].split('\x00')[0]) 
        # self.msg_body.setWordWrap(True)
        self.msg_body.setReadOnly(True)

        self.msg_body.document().adjustSize()
        

# =======
#         self.msg_body = QLabel(data['data'][0].split('\x00')[0]) 
#         self.msg_body.setWordWrap(True)
#         self.msg_body.adjustSize()
        msg_side = data['side'] if 'side' in data else  self.Left
        
        msg_tip = QLabel()
        msg_tip.setStyleSheet("QLabel{background-color:rgb(200,101,102);}")
        msg_tip.setFixedSize(20, 20)
        ly_msg = QHBoxLayout()
        if msg_side == self.Left:
            ly_msg.addWidget(msg_tip)
            ly_msg.addWidget(self.msg_body) 
            spacerItem = QtWidgets.QSpacerItem(20, 20, QtWidgets.QSizePolicy.Expanding,QtWidgets.QSizePolicy.Maximum)
            ly_msg.addItem(spacerItem)
        else:
            spacerItem = QtWidgets.QSpacerItem(20, 20, QtWidgets.QSizePolicy.Expanding,QtWidgets.QSizePolicy.Maximum)
            ly_msg.addItem(spacerItem)
            ly_msg.addWidget(self.msg_body) 
            ly_msg.addWidget(msg_tip)
        self.msg_body.setStyleSheet("background-color:rgb(0,255,255); font:10pt '楷体';border-width: 1px;border-style: solid;border-color: rgb(0, 100, 100);")
        self.setLayout(ly_msg)
    
def byte2value(data_bytes):
    size_unit = ['B','KB','MB','GB','TB','ZB'] 
    e = int(data_bytes)
    b = 0 
    while e >= 1024:
        e/=1024
        b += 1 
    return '%.2f%s'%(e, size_unit[b])

class process_bar(QProgressBar):
    def __init__(self,text=None):
        super(process_bar, self).__init__() 
        self.text = text 
        if text == None:
            text = '0%'
        self.pbar_text = QLabel(text, self)  
        self.pbar_text.setAttribute(Qt.WA_TranslucentBackground, False) 
        self.pbar_text.setAlignment(Qt.AlignHCenter|Qt.AlignVCenter) 
        self.valueChanged.connect(self.update_value) 

    def set_text(text):
        self.pbar_text.setText(text) 

    def update_value(self, value):
        if self.text  is  not None:
            return 
        self.pbar_text.setText('%d%%'%value)
    
    def resizeEvent(self, e):
        self.pbar_text.setGeometry(0,0,self.width(),self.height())

class msg_fdown_item(QWidget,msg_item):
    down_clickd = pyqtSignal(QWidget)
    def __init__(self, usr, data):
        super(msg_fdown_item, self).__init__() 
        self.msg_body = QWidget() 
        self.msg_body.setContentsMargins(0,0,0,0)
        ly_down = QVBoxLayout()
        ly_file = QHBoxLayout()
        ly_process = QHBoxLayout()
        self.pbar = process_bar() 
        self.sock = None
        # self.dwn_btn_txt = ['下载','取消','已取消','已完成'] 
        # self.btn_id = data['btn_id'] if 'btn_id' in data else 0
        self.dwn_btn = QPushButton('下载' if 'btn_txt' not in data else data['btn_txt']) #(self.dwn_btn_txt[self.btn_id])
        # self.dwn_btn.clicked.connect(self.down_clickd) 
        self.dwn_btn.clicked.connect(self.pg_dwn_btn) 
        # self.dwn_btn_stu = False 
        self.file_info = data 
        msg_tip = QLabel() 
        msg_tip.setStyleSheet("QLabel{background-color:rgb(200,101,102);}")
        msg_tip.setFixedSize(20, 20)
        # try:
        if True:
            fn = data['data'][1]
            fname = QLabel(fn)
            fname.setFixedHeight(15)
            fname.setFixedWidth(120)
            fname.setFrameStyle(QFrame.NoFrame)
            fname.setAlignment(Qt.AlignHCenter|Qt.AlignVCenter) 
            fname.setStyleSheet("border-color: rgba(0, 0, 0, 0);")
            fnes = QLabel()
            fnes.setFixedSize(1,15)
            if isinstance(data['data'][2], str):
                ts = int(data['data'][2],base=16)
            else:
                ts = data['data'][2]
            fsize = QLabel(byte2value(ts))
            fsize.setFixedHeight(15)
            fsize.setStyleSheet("border-color: rgba(0, 0, 0, 0);")
        # except Exception as e:
        #     print(e)
        #     print('data error:', data) 
        ly_file.addWidget(fname) 
        ly_file.addWidget(fnes) 
        ly_file.addWidget(fsize) 
        ly_process.addWidget(self.pbar) 
        ly_process.addWidget(self.dwn_btn) 
        ly_down.addLayout(ly_file) 

        fnes = QLabel()
        fnes.setFixedSize(190,1)
        ly_down.addWidget(fnes)

        ly_down.addLayout(ly_process) 
        self.msg_body.setFixedSize(200,50) 
        self.msg_body.setLayout(ly_down) 
        self.msg_body.setStyleSheet("background-color:rgb(0,255,255); font:10pt '楷体';border-width: 1px;border-style: solid;border-color: rgb(0, 100, 100);")
        ly_msg = QHBoxLayout() 

        self.setContentsMargins(0,0,0,0) 
        self.setFixedHeight(60) 
        msg_side = data['side'] if 'side' in data else  self.Left
        if msg_side == self.Left:
            ly_msg.addWidget(msg_tip)
            ly_msg.addWidget(self.msg_body) 
            spacerItem = QtWidgets.QSpacerItem(20, 20, QtWidgets.QSizePolicy.Expanding,QtWidgets.QSizePolicy.Maximum)
            ly_msg.addItem(spacerItem)
        elif msg_side == self.Right:
            spacerItem = QtWidgets.QSpacerItem(20, 20, QtWidgets.QSizePolicy.Expanding,QtWidgets.QSizePolicy.Maximum)
            ly_msg.addItem(spacerItem)
            ly_msg.addWidget(self.msg_body) 
            ly_msg.addWidget(msg_tip)
        self.setLayout(ly_msg) 
        self.pg_dwn = None
    
    def pg_btn_dw(self,w):pass

    def set_btn(self,text,prg=None):
        print('set btn:', text)
        if self.pg_dwn is not None:
            self.down_clickd.disconnect(self.pg_dwn)
        self.pg_dwn = self.pg_btn_dw if prg is None else prg
        self.down_clickd.connect(self.pg_dwn)
        self.dwn_btn.setText(text)

    def pg_dwn_btn(self):
        self.down_clickd.emit(self)

class MultiDirDialog(QFileDialog):
    def __init__(self, *args):
        QFileDialog.__init__(self, *args)
        self.setOption(self.DontUseNativeDialog, True)
        self.setFileMode(self.DirectoryOnly)

        self.tree = self.findChild(QTreeView)
        self.tree.setSelectionMode(QAbstractItemView.MultiSelection)

        self.list = self.findChild(QListView)
        self.list.setSelectionMode(QAbstractItemView.MultiSelection) 

class Windows(QMainWindow):
    add_usr = pyqtSignal(dict)
    add_msg = pyqtSignal(dict,dict)
    def __init__(self):
        super(Windows, self).__init__() 
        self.setWindowTitle('局域网聊天工具')
        self.resize(800,440)
        self.usr_list = QListWidget(self)
        self.usr_list.setGeometry(QtCore.QRect(10, 10, 250, self.height()-20))
        self.usr_list.itemClicked.connect(self.usr_click) 
        self.usr_data = {} 
        self.msg_list = QListWidget(self)
        self.msg_list.setGeometry(QtCore.QRect(270, 10, 520, self.height()-120))
        self.msg_list.itemClicked.connect(self.msg_click) 
        self.msg_data = {} 
        self.text_edit = QtWidgets.QTextEdit(self)
        self.text_edit.setGeometry(QtCore.QRect(270, self.height()-105, 450, 95))
        self.add_usr.connect(self.pg_add_usr) 
        self.add_msg.connect(self.pg_add_msg_t)  
        self.setMinimumWidth(800) 
        self.setMinimumWidth(600) 
        self.init_btns() 
        self.cur_usr = None
        self.msg_th = ipmsg.ipmsg(self)#threading.Thread(target=ipmsg.ipmsg_pg,args=(self,))
        self.msg_th.start() 
        self.msg_list.setStyleSheet("QScrollBar{width:10px;}")
        self.msg_list.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.msg_list.setViewMode(QListView.ListMode)

    def init_btns(self):
        self.btn_area = QWidget(self)
        self.btn_area.setGeometry(QtCore.QRect(725, self.height()-105, 65, 95))
        ly_btn = QVBoxLayout(self.btn_area)
        btn=QPushButton('文件')
        ly_btn.addWidget(btn)
        btn.clicked.connect(self.open_files)
        btn=QPushButton('文件夹')
        ly_btn.addWidget(btn)
        btn.clicked.connect(self.open_dirs)
        btn=QPushButton('清空')
        btn.clicked.connect(self.clear_editor)
        ly_btn.addWidget(btn)
        btn=QPushButton('发送')
        btn.clicked.connect(self.send_editor)
        ly_btn.addWidget(btn)
        self.btn_area.setLayout(ly_btn) 
        self.usr_item2addr = {}

    def clear_editor(self):
        self.text_edit.setText('')

    def send_editor(self):
        msg = self.text_edit.toPlainText()
        self.pg_add_msg({'usr_addr':self.cur_usr}, {'opt':0,'data':[msg], 'side':msg_item.Right})  #pt.IPMSG_FILEATTACHOPT
        print('no pack:',msg)
        self.msg_th.send_msg(self.cur_usr, msg)
        self.text_edit.setText('') 

    def open_files(self):
        dir_choose = MultiDirDialog.getOpenFileNames(self,  
                             "选取文件夹", '.') # 起始路径 
        print(dir_choose[0])
        if len(dir_choose[0]) <= 0:
            print("取消选择") 
            return 
        w = self.pg_add_msg({'usr_addr':self.cur_usr}, 
                {'opt':pt.IPMSG_FILEATTACHOPT,'data':['none',os.path.basename(dir_choose[0][0])+'...[%d]'%len(dir_choose[0]),
                        sum([self.msg_th.get_size(x) for x in dir_choose[0]])], 'side':msg_item.Right,'usr_addr':self.cur_usr}) 
        w.set_btn('取消', self.msg_th.upload_send_cancel)
        fs_id = self.msg_th.send_upload_file_info(self.msg_th.udp_sd,dir_choose[0],self.cur_usr,w) 
        if not fs_id[0] :
            print('每次最多只能分享10个文件')

        w.fs_id = fs_id[1]

    def open_dirs(self):
        dir_choose = MultiDirDialog.getExistingDirectory(self,  
                            "选取文件夹", '.') # 起始路径 
        print(dir_choose)

        if len(dir_choose) <= 0:
            print("取消选择") 
            return 
        w = self.pg_add_msg({'usr_addr':self.cur_usr}, 
                {'opt':pt.IPMSG_FILEATTACHOPT,'data':['none',os.path.basename(dir_choose[0]),
                        self.msg_th.get_size(dir_choose[0])], 'side':msg_item.Right,'usr_addr':self.cur_usr}) 
        w.set_btn('取消',self.msg_th.upload_send_cancel) 

        fs_id = self.msg_th.send_upload_file_info(self.msg_th.udp_sd,[dir_choose],self.cur_usr,w)  
        if not fs_id[0] :
            print('每次最多只能分享10个文件')
        w.fs_id = fs_id[1]

        

    def usr_item(self,usr, data):
        # 读取属性
        # ship_name = data['usr_name']
        if 'usr_photo' in data: 
            ship_photo = data['usr_photo'] 
        else:
            ship_photo = res_img.defult_usr_img #'icon/4.png' 
        ship_index = usr[0]  
        wight = QWidget()

        # 总体横向布局
        layout_main = QHBoxLayout()
        map_size = 30
        map_rl = QLabel()
        map_rl.setFixedSize(5, 1)
        map_l = QLabel()  # 头像显示
        map_l.setFixedSize(map_size, map_size)
        maps = QPixmap(ship_photo).scaled(map_size, map_size)
        map_l.setPixmap(maps)

        # 右边的纵向布局
        layout_right = QVBoxLayout()
        # 右边第一行
        layout_rline1 = QHBoxLayout()
        self.name_lb = QLabel(data['usr_name'])
        # data['msg_num'] = 111
        self.msg_num = QLabel('' if 'msg_num' not in data or data['msg_num'] <= 0 else str(data['msg_num']) if data['msg_num']<=99 else '99+')
        if not('msg_num' not in data or data['msg_num'] <= 0) :  
            self.msg_num.setStyleSheet("background-color:rgb(255,0,0); font:10pt '楷体';border-width: 1px;border-style: solid; border-color: rgb(200, 0, 100);")
            self.msg_num.setFixedWidth(30)
        layout_rline1.addWidget(self.name_lb)
        layout_rline1.addWidget(self.msg_num)

        # 右下的的横向布局
        layout_right_down = QHBoxLayout()  # 右下的横向布局
        layout_right_down.addWidget(QLabel(ship_index))

        # 按照从左到右, 从上到下布局添加
        layout_main.addWidget(map_l)  # 最左边的头像
        layout_main.addWidget(map_rl)
        # layout_right.addWidget(QLabel(data['usr_name']))  # 右边的纵向布局 
        layout_right.addLayout(layout_rline1)
        layout_right.addLayout(layout_right_down)  # 右下角横向布局
        layout_main.addLayout(layout_right)  # 右边的布局
        wight.setLayout(layout_main)  # 布局给wight
        return wight  # 返回wight

    def pg_add_usr(self, data): 
        if self.cur_usr is None:
            self.cur_usr = data['usr_addr'] 
        if data['usr_addr'] in self.usr_data:
            return
        item = usr_list_item()  # 创建QListWidgetItem对象 
        item.setSizeHint(QSize(150, 50))  # 设置QListWidgetItem大小 
        usr = data['usr_addr'] 
        widget = self.usr_item(usr, data)  # 调用上面的函数获取对应 
        self.usr_list.addItem(item)  # 添加item 
        self.usr_list.setItemWidget(item, widget)  # 为item设置widget 
        data['widget'] = widget
        self.usr_data[data['usr_addr']] = data 

        # print(self.usr_data)
        self.usr_item2addr[item] = data

    def pg_add_msg_t(self, usr, data): 
        w = self.pg_add_msg(usr, data)
        if data['opt'] & pt.IPMSG_FILEATTACHOPT: # msg_fdown_item 
            w.set_btn('下载', self.file_down)

    def pg_add_msg(self, usr, data): 
        item = usr_list_item()  # 创建QListWidgetItem对象 
        item.setSizeHint(QSize(150, 50))  # 设置QListWidgetItem大小 
        if data['opt'] & pt.IPMSG_FILEATTACHOPT: # msg_fdown_item 
            widget = msg_fdown_item(usr, data) 
            # widget.down_clickd.connect(self.file_down)
        else:
            widget = msg_txt_item(usr, data)  # 调用上面的函数获取对应 
            d = widget.msg_body.document()
            widget.setFixedHeight(d.size().height()+10)

        # print('usr:',usr, self.cur_usr)
        if usr['usr_addr'] == self.cur_usr:
            self.msg_list.addItem(item)  # 添加item 
            if data['opt'] & pt.IPMSG_FILEATTACHOPT:
                item.setSizeHint(QSize(200, widget.height()))
            self.msg_list.setItemWidget(item, widget)  # 为item设置widget 
            
            mlitem = self.msg_list.itemWidget(item) 
            if mlitem.msg_body.width() > 400:
                mlitem.msg_body.setFixedWidth(400)
            if not data['opt'] & pt.IPMSG_FILEATTACHOPT:
                mlitem.msg_body.setFixedHeight(d.size().height()+10)
            
        if usr['usr_addr'] not in self.msg_data:
            self.msg_data[usr['usr_addr']] = {} 
            print('build msg',usr['usr_addr'])
        t = self.msg_data[usr['usr_addr']]
        t[item] = data 
        return widget

    def usr_click(self, item):
        # print('usr click:',self.usr_item2addr[item])
        self.msg_list.clear() 

        self.cur_usr = self.usr_item2addr[item]['usr_addr'] 
        if self.cur_usr in self.msg_data:
            t = self.msg_data[self.cur_usr]
            self.msg_data[self.cur_usr] = {}
            for e in t:
                self.pg_add_msg(self.usr_item2addr[item], t[e]) 
    def file_down(self, msg_item):#down_clickd 
        # if not msg_item.dwn_btn_stu:
        dir_choose = QFileDialog.getExistingDirectory(self,  
                            "选取文件夹",  
                            '.') # 起始路径
        if dir_choose == "":
            print("取消选择") 
            return 
        msg_item.file_info['save_dir'] = dir_choose 
        self.msg_th.send_down_file_msg(self.cur_usr, msg_item) 
        msg_item.set_btn('取消', self.msg_th.fdown_cancel)
        # else:
        #     msg_item.file_info['save_dir'] = dir_choose 
        # msg_item.btn_id  += 1# msg_item.dwn_btn_stu self.btn_id


    def msg_click(self, item):
        it = self.msg_list.itemWidget(item) 
        # print('msg click:',self.msg_data[self.cur_usr][item])

    # # 添加 复写 窗口状态改变 函数
    # def changeEvent(self, e):
    #     if e.type() == QtCore.QEvent.WindowStateChange:
    #         if self.isMinimized():
    #             print("窗口最小化")
    #         elif self.isMaximized():
    #             print("窗口最大化")
    #         elif self.isFullScreen():
    #             print("全屏显示")
    #         elif self.isActiveWindow():
    #             print("活动窗口")
 
    # # 添加 复写 显示窗口函数
    # def showEvent(self, e):
    #     print("窗口显示")

    # def hideEvent(self, e):
    #     print("窗口隐藏")

    def closeEvent(self,e):
        self.msg_th.exit()
        self.msg_th.join()
        # print ("窗口关闭")

    # def moveEvent(self,e):
    #    print ("窗口移动")

    def resizeEvent(self,e):
        self.usr_list.setFixedHeight(self.height()-20)
        self.msg_list.setFixedHeight(self.height()-120)
        self.text_edit.setGeometry(QtCore.QRect(270, self.height()-105, 450, 95))
        self.btn_area.setGeometry(QtCore.QRect(725, self.height()-105, 65, 95))
        # self.init_btns()

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    windows = Windows()
    windows.show()
    sys.exit(app.exec_())
    